const ui5Autocomplete = require("./ui5-autocomplete.json");

// xml-hint.js
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE

(function(mod) {
    // Plain browser env
    mod(CodeMirror);
  })(function(CodeMirror) {
    "use strict";

    var Pos = CodeMirror.Pos;

    function matches(hint, typed, matchInMiddle) {
      if (matchInMiddle) return hint.indexOf(typed) >= 0;
      else return hint.lastIndexOf(typed, 0) == 0;
    }

    function getHints(cm, options) {
      var tags = options && options.schemaInfo;
      var quote = (options && options.quoteChar) || '"';
      var matchInMiddle = options && options.matchInMiddle;
      if (!tags) return;
      var cur = cm.getCursor(), token = cm.getTokenAt(cur);
      if (token.end > cur.ch) {
        token.end = cur.ch;
        token.string = token.string.slice(0, cur.ch - token.start);
      }
      var inner = CodeMirror.innerMode(cm.getMode(), token.state);
      if (!inner.mode.xmlCurrentTag) return
      var result = [], replaceToken = false, prefix;
      var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
      var tagName = tag && /^\w/.test(token.string), tagStart;

      if (tagName) {
        var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
        var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
        if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
      } else if (tag && token.string == "<") {
        tagType = "open";
      } else if (tag && token.string == "</") {
        tagType = "close";
      }

      var tagInfo = inner.mode.xmlCurrentTag(inner.state)
      if (!tag && !tagInfo || tagType) {
        if (tagName)
          prefix = token.string;
        replaceToken = tagType;
        var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : []
        var inner = context.length && context[context.length - 1]
        var curTag = inner && tags[inner]
        var childList = inner ? curTag && curTag.children : tags["!top"];
        if (childList && tagType != "close") {
          for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle))
            result.push("<" + childList[i]);
        } else if (tagType != "close") {
          for (var name in tags)
            if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle)))
              result.push("<" + name);
        }
        if (inner && (!prefix || tagType == "close" && matches(inner, prefix, matchInMiddle)))
          result.push("</" + inner + ">");
      } else {
        // Attribute completion
        var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs;
        var globalAttrs = tags["!attrs"];
        if (!attrs && !globalAttrs) return;
        if (!attrs) {
          attrs = globalAttrs;
        } else if (globalAttrs) { // Combine tag-local and global attributes
          var set = {};
          for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
          for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
          attrs = set;
        }
        if (token.type == "string" || token.string == "=") { // A value
          var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
                                   Pos(cur.line, token.type == "string" ? token.start : token.end));
          var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
          if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
          if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
          if (token.type == "string") {
            prefix = token.string;
            var n = 0;
            if (/['"]/.test(token.string.charAt(0))) {
              quote = token.string.charAt(0);
              prefix = token.string.slice(1);
              n++;
            }
            var len = token.string.length;
            if (/['"]/.test(token.string.charAt(len - 1))) {
              quote = token.string.charAt(len - 1);
              prefix = token.string.substr(n, len - 2);
            }
            if (n) { // an opening quote
              var line = cm.getLine(cur.line);
              if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote
            }
            replaceToken = true;
          }
          var returnHintsFromAtValues = function(atValues) {
            if (atValues)
              for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle))
                result.push(quote + atValues[i] + quote);
            return returnHints();
          };
          if (atValues && atValues.then) return atValues.then(returnHintsFromAtValues);
          return returnHintsFromAtValues(atValues);
        } else { // An attribute name
          if (token.type == "attribute") {
            prefix = token.string;
            replaceToken = true;
          }
          for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle)))
            result.push(attr);
        }
      }
      function returnHints() {
        return {
          list: result,
          from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
          to: replaceToken ? Pos(cur.line, token.end) : cur
        };
      }
      return returnHints();
    }

    CodeMirror.registerHelper("hint", "xml", getHints);
  });

// html-hint.js
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE

(function(mod) {
    // Plain browser env
    mod(CodeMirror);
  })(function(CodeMirror) {
    "use strict";

    var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
    var targets = ["_blank", "_self", "_top", "_parent"];
    var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
    var methods = ["get", "post", "put", "delete"];
    var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
    var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
                 "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
                 "orientation:landscape", "device-height: [X]", "device-width: [X]"];
    var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags

    var data = {
      a: {
        attrs: {
          href: null, ping: null, type: null,
          media: media,
          target: targets,
          hreflang: langs
        }
      },
      abbr: s,
      acronym: s,
      address: s,
      applet: s,
      area: {
        attrs: {
          alt: null, coords: null, href: null, target: null, ping: null,
          media: media, hreflang: langs, type: null,
          shape: ["default", "rect", "circle", "poly"]
        }
      },
      article: s,
      aside: s,
      audio: {
        attrs: {
          src: null, mediagroup: null,
          crossorigin: ["anonymous", "use-credentials"],
          preload: ["none", "metadata", "auto"],
          autoplay: ["", "autoplay"],
          loop: ["", "loop"],
          controls: ["", "controls"]
        }
      },
      b: s,
      base: { attrs: { href: null, target: targets } },
      basefont: s,
      bdi: s,
      bdo: s,
      big: s,
      blockquote: { attrs: { cite: null } },
      body: s,
      br: s,
      button: {
        attrs: {
          form: null, formaction: null, name: null, value: null,
          autofocus: ["", "autofocus"],
          disabled: ["", "autofocus"],
          formenctype: encs,
          formmethod: methods,
          formnovalidate: ["", "novalidate"],
          formtarget: targets,
          type: ["submit", "reset", "button"]
        }
      },
      canvas: { attrs: { width: null, height: null } },
      caption: s,
      center: s,
      cite: s,
      code: s,
      col: { attrs: { span: null } },
      colgroup: { attrs: { span: null } },
      command: {
        attrs: {
          type: ["command", "checkbox", "radio"],
          label: null, icon: null, radiogroup: null, command: null, title: null,
          disabled: ["", "disabled"],
          checked: ["", "checked"]
        }
      },
      data: { attrs: { value: null } },
      datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
      datalist: { attrs: { data: null } },
      dd: s,
      del: { attrs: { cite: null, datetime: null } },
      details: { attrs: { open: ["", "open"] } },
      dfn: s,
      dir: s,
      div: s,
      dialog: { attrs: { open: null } },
      dl: s,
      dt: s,
      em: s,
      embed: { attrs: { src: null, type: null, width: null, height: null } },
      eventsource: { attrs: { src: null } },
      fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
      figcaption: s,
      figure: s,
      font: s,
      footer: s,
      form: {
        attrs: {
          action: null, name: null,
          "accept-charset": charsets,
          autocomplete: ["on", "off"],
          enctype: encs,
          method: methods,
          novalidate: ["", "novalidate"],
          target: targets
        }
      },
      frame: s,
      frameset: s,
      h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
      head: {
        attrs: {},
        children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
      },
      header: s,
      hgroup: s,
      hr: s,
      html: {
        attrs: { manifest: null },
        children: ["head", "body"]
      },
      i: s,
      iframe: {
        attrs: {
          src: null, srcdoc: null, name: null, width: null, height: null,
          sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
          seamless: ["", "seamless"]
        }
      },
      img: {
        attrs: {
          alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
          crossorigin: ["anonymous", "use-credentials"]
        }
      },
      input: {
        attrs: {
          alt: null, dirname: null, form: null, formaction: null,
          height: null, list: null, max: null, maxlength: null, min: null,
          name: null, pattern: null, placeholder: null, size: null, src: null,
          step: null, value: null, width: null,
          accept: ["audio/*", "video/*", "image/*"],
          autocomplete: ["on", "off"],
          autofocus: ["", "autofocus"],
          checked: ["", "checked"],
          disabled: ["", "disabled"],
          formenctype: encs,
          formmethod: methods,
          formnovalidate: ["", "novalidate"],
          formtarget: targets,
          multiple: ["", "multiple"],
          readonly: ["", "readonly"],
          required: ["", "required"],
          type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
                 "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
                 "file", "submit", "image", "reset", "button"]
        }
      },
      ins: { attrs: { cite: null, datetime: null } },
      kbd: s,
      keygen: {
        attrs: {
          challenge: null, form: null, name: null,
          autofocus: ["", "autofocus"],
          disabled: ["", "disabled"],
          keytype: ["RSA"]
        }
      },
      label: { attrs: { "for": null, form: null } },
      legend: s,
      li: { attrs: { value: null } },
      link: {
        attrs: {
          href: null, type: null,
          hreflang: langs,
          media: media,
          sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
        }
      },
      map: { attrs: { name: null } },
      mark: s,
      menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
      meta: {
        attrs: {
          content: null,
          charset: charsets,
          name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
          "http-equiv": ["content-language", "content-type", "default-style", "refresh"]
        }
      },
      meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
      nav: s,
      noframes: s,
      noscript: s,
      object: {
        attrs: {
          data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
          typemustmatch: ["", "typemustmatch"]
        }
      },
      ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
      optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
      option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
      output: { attrs: { "for": null, form: null, name: null } },
      p: s,
      param: { attrs: { name: null, value: null } },
      pre: s,
      progress: { attrs: { value: null, max: null } },
      q: { attrs: { cite: null } },
      rp: s,
      rt: s,
      ruby: s,
      s: s,
      samp: s,
      script: {
        attrs: {
          type: ["text/javascript"],
          src: null,
          async: ["", "async"],
          defer: ["", "defer"],
          charset: charsets
        }
      },
      section: s,
      select: {
        attrs: {
          form: null, name: null, size: null,
          autofocus: ["", "autofocus"],
          disabled: ["", "disabled"],
          multiple: ["", "multiple"]
        }
      },
      small: s,
      source: { attrs: { src: null, type: null, media: null } },
      span: s,
      strike: s,
      strong: s,
      style: {
        attrs: {
          type: ["text/css"],
          media: media,
          scoped: null
        }
      },
      sub: s,
      summary: s,
      sup: s,
      table: s,
      tbody: s,
      td: { attrs: { colspan: null, rowspan: null, headers: null } },
      textarea: {
        attrs: {
          dirname: null, form: null, maxlength: null, name: null, placeholder: null,
          rows: null, cols: null,
          autofocus: ["", "autofocus"],
          disabled: ["", "disabled"],
          readonly: ["", "readonly"],
          required: ["", "required"],
          wrap: ["soft", "hard"]
        }
      },
      tfoot: s,
      th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
      thead: s,
      time: { attrs: { datetime: null } },
      title: s,
      tr: s,
      track: {
        attrs: {
          src: null, label: null, "default": null,
          kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
          srclang: langs
        }
      },
      tt: s,
      u: s,
      ul: s,
      "var": s,
      video: {
        attrs: {
          src: null, poster: null, width: null, height: null,
          crossorigin: ["anonymous", "use-credentials"],
          preload: ["auto", "metadata", "none"],
          autoplay: ["", "autoplay"],
          mediagroup: ["movie"],
          muted: ["", "muted"],
          controls: ["", "controls"]
        }
      },
      wbr: s,
      "ui5-button": {
        attrs: {
            design: ["Positive", "Negative", "Default", "Emphasized"]
        }
      }
    };

    // data = {...data, ...ui5Autocomplete};
    data = Object.assign(data, ui5Autocomplete);

    var globalAttrs = {
      accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
      "class": null,
      contenteditable: ["true", "false"],
      contextmenu: null,
      dir: ["ltr", "rtl", "auto"],
      draggable: ["true", "false", "auto"],
      dropzone: ["copy", "move", "link", "string:", "file:"],
      hidden: ["hidden"],
      id: null,
      inert: ["inert"],
      itemid: null,
      itemprop: null,
      itemref: null,
      itemscope: ["itemscope"],
      itemtype: null,
      lang: ["en", "es"],
      spellcheck: ["true", "false"],
      autocorrect: ["true", "false"],
      autocapitalize: ["true", "false"],
      style: null,
      tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
      title: null,
      translate: ["yes", "no"],
      onclick: null,
      rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
    };
    function populate(obj) {
      for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
        obj.attrs[attr] = globalAttrs[attr];
    }

    populate(s);
    for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
      populate(data[tag]);

    CodeMirror.htmlSchema = data;
    function htmlHint(cm, options) {
      var local = {schemaInfo: data};
      if (options) for (var opt in options) local[opt] = options[opt];
      return CodeMirror.hint.xml(cm, local);
    }
    CodeMirror.registerHelper("hint", "html", htmlHint);
    // add to window for usage in playground-code-editor
    window.htmlHint = htmlHint;
  });